﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Services.Protocols;
using Config;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Crm.Helper;
using Crm.CrmService;
using QueueManagerHelper;
using QueueManagerHelper.Exchange2007;
using Ionic.Zip;

namespace QueueManagerService
{
    public class QueueMonitor
    {
        private CRMHelper _crmHelper = null;
        private Exchange2007Helper _exchangeHelper = null;
        private Regex _caseIdentifierRegEx;
        private Regex _caseMailRegExParser;
        private Dictionary<string, Regex[]> _htmlCleanerExpressions = new Dictionary<string, Regex[]>();
        private QueueSetting _queueSettings;
        private bool _sendEmails = true;
        private string _cleanEmailDescription = string.Empty;
        //Work in progress queue of the owner of the queue
        private queue _queueOwnerWipQueue = null;

        //list of email addresses to ignore mail from
        public List<string> IgnoreEmailFrom { get; set; }

        //list of words which, if contained in subject of email, will cause email to be ignored
        public List<string> IgnoreWhenSubjectContains { get; set; }

        public Dictionary<string, Exchange2007Helper> ExchangeHelpers { get; set; }

        public CRMHelper CRMHelper
        {
            get
            {
                if (_crmHelper == null)
                    _crmHelper = new CRMHelper(ConfigurationManager.AppSettings.Get("organization"),
                                            ConfigurationManager.AppSettings.Get("username"),
                                            ConfigurationManager.AppSettings.Get("password"),
                                            ConfigurationManager.AppSettings.Get("domain"),
                                            ConfigurationManager.AppSettings.Get("crmserviceurl"),
                                            ConfigurationManager.AppSettings.Get("crmserviceurl").ToLower().Replace("crmservice.asmx", "metadataservice.asmx")
                                           );
                return _crmHelper;
            }
            set
            {

            }
        }

        public QueueMonitor()
        {
            string functionName = GetType().ToString() + " QueueMonitor constructor: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Initializing reg expressions: they take a lot of setting up & are time consuming if done multiple times
                SetupGlobalRegularExpressions();
            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void SetupGlobalRegularExpressions()
        {
            string functionName = GetType().ToString() + " SetupGlobalRegularExpressions: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Reg exp that will help in parsing of mails being sent out
                _caseMailRegExParser = new Regex("{#\\S*#}", RegexOptions.IgnoreCase | RegexOptions.Compiled);
                //Reg exp that will match "[casesuffix]-[n digits]-[alpha uniquespecifier]" from org settings.
                string caseRegEx = "\\b" + CRMHelper.Organization.caseprefix + "\\b-\\d+-\\b\\w{" + CRMHelper.Organization.uniquespecifierlength.Value.ToString() + "}\\b";
                _caseIdentifierRegEx = new Regex(caseRegEx, RegexOptions.IgnoreCase | RegexOptions.Compiled);

                string[] tagsToReplace = new string[] { "head", "script", "style" };
                foreach (string tag in tagsToReplace)
                {
                    Regex[] tagExpressions = 
                    {
                        new Regex("<( )*" + tag + "([^>])*>", RegexOptions.IgnoreCase | RegexOptions.Compiled),
                        new Regex("(<( )*(/)( )*" + tag + "( )*>)", RegexOptions.IgnoreCase | RegexOptions.Compiled), 
                        new Regex("(<" + tag + ">).*(</" + tag + ">)", RegexOptions.IgnoreCase | RegexOptions.Compiled)
                    };
                    _htmlCleanerExpressions.Add(tag, tagExpressions);
                }
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        #region "Processing of queues"

        public void ProcessQueue(QueueSetting QueueSettings)
        {
            string functionName = GetType().ToString() + " ProcessQueue: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                _queueSettings = QueueSettings;
                Logger.Write(functionName + "Processing queue: " + QueueSettings.Name, "Trace", 1);
                queue queue = (queue)CRMHelper.GetSingleData(EntityName.queue.ToString(), QueueSettings.QueueID, new string[] { "primaryuserid", "emailaddress", "name" });
                if (queue == null)
                    throw new Exception("Could not find queue " + QueueSettings.Name);
                //Get the owner of the queue: will be used later in RouteCase and/or in subsequent calls
                if (_queueOwnerWipQueue == null || _queueOwnerWipQueue.primaryuserid != queue.primaryuserid)
                    GetQueueOwnerWIPQueue(queue);

                if (_queueSettings.RouteEmailsFromExchange)
                    RouteEmailsFromExchange(queue);

                BusinessEntity[] queueResult = CRMHelper.GetEntitiesByAttributes(
                                                    EntityName.queueitem.ToString(), 
                                                    new string[] { "queueid", "objecttypecode" },
                                                    new string[] { _queueSettings.QueueID, EntityName.email.ToString() });

                if (queueResult != null)
                    ProcessQueueItems(queue, queueResult);
            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void ProcessQueueItems(queue Queue, BusinessEntity[] QueueResult)
        {
            string functionName = GetType().ToString() + " ProcessQueueItems: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                foreach (queueitem queueItem in QueueResult)
                {
                    email email = null;
                    try
                    {
                        email = (email)CRMHelper.GetSingleData(EntityName.email.ToString(), queueItem.objectid.Value.ToString());
                        Logger.Write(functionName + "Processing email: " + email.subject, "Trace", 1);
                        //Update email's header field with headers retrieved from Exchange - only if not previously filled in
                        //Logger.Write(functionName + "Current headers: " + email.new_headers, "Trace", 1);
                        //if (_queueSettings.GetEmailHeadersFromExchange && string.IsNullOrEmpty(email.new_headers))
                        //    UpdateEmailHeaders(email);
                        //Update subject of email if not filled in by customer
                        if (string.IsNullOrEmpty(email.subject))
                            email.subject = "No subject.";
                        if (CreateNewCaseFromEmail(Queue, email))
                            ProcessQueueItem(Queue, email);
                    }
                    catch (Exception procEx)
                    {
                        Logger.Write(functionName + "Processing Error: " + procEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                        try
                        {
                            //Try to move the email to the error queue
                            RouteEmailToQueue(email.activityid.Value, new Guid(_queueSettings.ErrorQueueID));
                        }
                        catch (Exception errorEx)
                        {
                            Logger.Write(functionName + "Could not save processing of queue: " + errorEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                            throw;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void ProcessQueueItem(queue Queue, email Email)
        {
            string functionName = GetType().ToString() + " ProcessQueueItem: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                _sendEmails = true;
                IncomingEntity incomingEntity = SetupIncomingEntity(Email);
                if (incomingEntity.Type == EntityName.contact.ToString() || incomingEntity.Type == EntityName.account.ToString())
                    ProcessEmail(incomingEntity, Email, Queue);
                else
                    throw new Exception("Could not process email: " + Email.subject);
                SetEmailStatusToCompleted(Email.activityid.Value);
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void ProcessEmail(IncomingEntity IncomingEntity, email Email, queue Queue)
        {
            string functionName = GetType().ToString() + " ProcessEmail: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Clean the email's html here because it's used in multiple places and takes time
                if (Email.description != null)
                    _cleanEmailDescription = UtilityHelper.CleanHTML(Email.description, _htmlCleanerExpressions);
                else
                    _cleanEmailDescription = string.Empty;
                //Create the case
                Guid caseId = CreateCase(IncomingEntity, Email, Queue);
                //Retrieve it to get poplulated defaults or result of workflows etc
                incident createdCase = (incident)CRMHelper.GetSingleData(EntityName.incident.ToString(), caseId.ToString());
                //Update case's title to include the ticket number
                if (_queueSettings.UpdateCaseTitle)
                    UpdateCaseTitle(caseId);
                //Route case from queue in which it has been created to correct queue
                RouteCase(caseId);
                //Set the "regarding" details on the email
                UpdateEmailWithCaseDetails(Email, caseId);
                //Resolve contacts in email
                ResolveEmailContacts(Email, IncomingEntity);
                //Send notifications to agents/clients
                if (_sendEmails)
                    SendCaseEmails(IncomingEntity, Email, Queue, createdCase);
                //Finally remove the email from the queue now that the case has taken its place: 
                //  done last because up until now we could still move the email to the error queue if an error occurs in the above code
                DetachEmailFromQueue(Email, _queueSettings.QueueID);
            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void ResolveEmailContacts(email Email, IncomingEntity IncomingEntity)
        {
            string functionName = GetType().ToString() + " ResolveEmailContacts: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                if (Email.cc.GetLength(0) > 0)
                {
                    List<activityparty> resolvedContacts = ResolveEmailCCContacts(Email);
                    if (resolvedContacts.Count > 0)
                        Email.cc = resolvedContacts.ToArray();
                    else
                        Email.cc = null;
                }
                Email.from[0].partyid = new Lookup();
                Email.from[0].partyid.type = IncomingEntity.Type;
                Email.from[0].partyid.Value = IncomingEntity.ID;

                CRMHelper.CallCRMMethod(Email, TypeOfCRMRequest.Update);
            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private List<activityparty> ResolveEmailCCContacts(email Email)
        {
            string functionName = GetType().ToString() + " ResolveEmailCCContacts: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                Dictionary<string, string> exchangeNames = new Dictionary<string, string>();
                if (_queueSettings.GetContactNameFromExchange)
                {
                    if (string.IsNullOrEmpty(Email.submittedby))
                        exchangeNames = _exchangeHelper.GetEmailCCNames(Email.from[0].addressused, Email.subject);
                    else
                        exchangeNames = _exchangeHelper.GetEmailCCNames(Email.submittedby, Email.subject);
                }

                List<activityparty> resolvedContacts = new List<activityparty>();
                foreach (activityparty ccRecipient in Email.cc)
                {
                    contact contact = GetContactFromEmailAddress(ccRecipient.addressused);
                    Guid contactId;
                    if (contact == null)
                    {
                        NameStruct recipientName = GetCCRecipientName(Email, exchangeNames, ccRecipient);
                        contact = new contact();
                        contact.firstname = CRMHelper.FormatEntityAttributeValue(EntityName.contact.ToString(), "firstname", recipientName.FirstName);
                        contact.lastname = CRMHelper.FormatEntityAttributeValue(EntityName.contact.ToString(), "lastname", recipientName.LastName);
                        contact.emailaddress1 = CRMHelper.FormatEntityAttributeValue(EntityName.contact.ToString(), "emailaddress1", ccRecipient.addressused);
                        contactId = CRMHelper.CallCRMMethod(contact, TypeOfCRMRequest.Create);
                    }
                    else
                        contactId = contact.contactid.Value;

                    activityparty resolvedAP = new activityparty();
                    resolvedAP.addressused = ccRecipient.addressused;
                    resolvedAP.partyid = new Lookup();
                    resolvedAP.partyid.type = EntityName.contact.ToString();
                    resolvedAP.partyid.Value = contactId;
                    resolvedAP.participationtypemask = new Picklist();
                    resolvedAP.participationtypemask.Value = 3;
                    resolvedContacts.Add(resolvedAP);
                }
                return resolvedContacts;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Warning: " + ex.ToString(), "Warning", 4, 999, TraceEventType.Warning);
                return new List<activityparty>();
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private static NameStruct GetCCRecipientName(email Email, Dictionary<string, string> exchangeNames, activityparty ccRecipient)
        {
            try
            {
                NameStruct recipientName = new NameStruct();

                string exchangeName = string.Empty;
                if (exchangeNames.ContainsKey(ccRecipient.addressused))
                    exchangeName = exchangeNames[ccRecipient.addressused];

                if (string.IsNullOrEmpty(exchangeName))
                {
                    if (string.IsNullOrEmpty(Email.submittedby))
                        recipientName = UtilityHelper.ResolveNames(Email.from[0].addressused);
                    else
                        recipientName = UtilityHelper.ResolveNames(Email.submittedby);
                }
                else
                {
                    if (exchangeName.Contains("@"))
                        recipientName = UtilityHelper.ResolveNames(exchangeName);
                    else if (exchangeName.Contains(" "))
                    {
                        recipientName.FirstName = exchangeName.Split(' ')[0].Trim();
                        recipientName.LastName = exchangeName.Substring(exchangeName.IndexOf(' ')).Trim();
                    }
                    else
                    {
                        recipientName.FirstName = exchangeName;
                        recipientName.LastName = string.Empty;
                    }
                }
                return recipientName;
            }
            catch (Exception)
            {
                return new NameStruct();
            }
        }

        private Guid CreateCase(IncomingEntity IncomingEntity, email Email, queue Queue)
        {
            string functionName = GetType().ToString() + " CreateCase: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                DynamicEntity caseEntity = new DynamicEntity();
                //Set the properties on the new case entity
                SetCaseProperties(IncomingEntity, Email, Queue, caseEntity);
                //Update dynamic properties
                SetCaseDynamicProperties(caseEntity);
                //Create the target.
                TargetCreateDynamic targetCreateDynamic = new TargetCreateDynamic();
                targetCreateDynamic.Entity = caseEntity;
                //Create the request object.
                CreateRequest createRequest = new CreateRequest();
                //Set the properties of the request object.
                createRequest.Target = targetCreateDynamic;
                //Execute the request.
                return CRMHelper.CallCRMMethod(createRequest, TypeOfCRMRequest.Execute);
            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private contact CreateContact(email Email)
        {
            string functionName = GetType().ToString() + " CreateContact: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                NameStruct contactName = GetContactNameFromEmail(Email);
                contact contact = new contact();
                contact.firstname = CRMHelper.FormatEntityAttributeValue(EntityName.contact.ToString(), "firstname", contactName.FirstName);
                contact.lastname = CRMHelper.FormatEntityAttributeValue(EntityName.contact.ToString(), "lastname", contactName.LastName);
                contact.emailaddress1 = CRMHelper.FormatEntityAttributeValue(EntityName.contact.ToString(), "emailaddress1", Email.from[0].addressused);
                Guid contactId = CRMHelper.CallCRMMethod(contact, TypeOfCRMRequest.Create);
                return (contact)CRMHelper.GetSingleData(EntityName.contact.ToString(), contactId.ToString());
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        #endregion

        #region "Utility functions"

        private string ParseCaseMail(string TextToParse, incident CaseEntity, IncomingEntity IncomingEntity, email Email, queue Queue)
        {
            string functionName = GetType().ToString() + " ParseCaseMail: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            //Logger.Write(functionName + "Parameter: TextToParse: " + TextToParse, "Trace", 1);
            try
            {
                string parsedText;
                //Parse and replace information used in the e-mail. First the old way for backward compatibility...
                parsedText = TextToParse.Replace("{ticketnumber}", CaseEntity.ticketnumber);
                parsedText = parsedText.Replace("{title}", CaseEntity.title);
                parsedText = parsedText.Replace("{firstname}", IncomingEntity.FirstName);
                parsedText = parsedText.Replace("{lastname}", IncomingEntity.LastName);
                parsedText = parsedText.Replace("{sender}", Email.from[0].addressused);
                parsedText = parsedText.Replace("{caseid}", CaseEntity.incidentid.Value.ToString().ToLower());
                parsedText = parsedText.Replace("{queuename}", Queue.name);
                parsedText = parsedText.Replace("{emailbodytext}", _cleanEmailDescription);
                parsedText = parsedText.Replace("{emailbodyhtml}", _cleanEmailDescription);
                parsedText = parsedText.Replace("{emailbodyhtmluncleaned}", Email.description);
                parsedText = parsedText.Replace("{emailsubject}", Email.subject);
                parsedText = parsedText.Replace("{date}", DateTime.Now.ToShortDateString());
                parsedText = parsedText.Replace("{time}", DateTime.Now.ToShortTimeString());
                parsedText = parsedText.Replace("{emailid}", Email.activityid.Value.ToString().ToLower());
                parsedText = parsedText.Replace("{crmurl}", ConfigurationManager.AppSettings.Get("crmurl"));
                return parsedText;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private NameStruct GetContactNameFromEmail(email Email)
        {
            string functionName = GetType().ToString() + " GetContactNameFromEmail: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                NameStruct returnName = new NameStruct();

                string exchangeName = string.Empty;
                if (_queueSettings.GetContactNameFromExchange)
                {
                    if (string.IsNullOrEmpty(Email.submittedby))
                        exchangeName = _exchangeHelper.GetEmailName(Email.from[0].addressused, Email.subject);
                    else
                        exchangeName = _exchangeHelper.GetEmailName(Email.submittedby, Email.subject);
                    Logger.Write(functionName + "Name returned from Exchange: " + exchangeName, "Trace", 1);
                }
                if (exchangeName == string.Empty)
                {
                    if (string.IsNullOrEmpty(Email.submittedby))
                        returnName = UtilityHelper.ResolveNames(Email.from[0].addressused);
                    else
                        returnName = UtilityHelper.ResolveNames(Email.submittedby);
                }
                else
                {
                    if (exchangeName.Contains(" "))
                    {
                        returnName.FirstName = exchangeName.Split(' ')[0].Trim();
                        returnName.LastName = exchangeName.Substring(exchangeName.IndexOf(' ')).Trim();
                    }
                    else
                    {
                        returnName.FirstName = exchangeName;
                        returnName.LastName = string.Empty;
                    }
                }
                Logger.Write(functionName + "Returning: returnName.FirstName: " + returnName.FirstName, "Trace", 1);
                Logger.Write(functionName + "Returning: returnName.LastName: " + returnName.LastName, "Trace", 1);
                return returnName;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }



        #endregion

        #region "Querying of CRM entities"

        private contact GetContactFromEmailAddress(string EmailAddress)
        {
            string functionName = GetType().ToString() + " GetContactFromEmailAddress: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {

                QueryExpression query = new QueryExpression();

                query.EntityName = "contact";
                query.ColumnSet = new AllColumns();

                query.Criteria = new FilterExpression();
                query.Criteria.FilterOperator = LogicalOperator.Or;

                ConditionExpression condition1 = new ConditionExpression();
                condition1.AttributeName = "emailaddress1";
                condition1.Operator = ConditionOperator.Equal;
                condition1.Values = new object[] { EmailAddress };

                ConditionExpression condition2 = new ConditionExpression();
                condition2.AttributeName = "emailaddress2";
                condition2.Operator = ConditionOperator.Equal;
                condition2.Values = new object[] { EmailAddress };

                ConditionExpression condition3 = new ConditionExpression();
                condition3.AttributeName = "emailaddress3";
                condition3.Operator = ConditionOperator.Equal;
                condition3.Values = new object[] { EmailAddress };

                query.Criteria.Conditions = new ConditionExpression[] { condition1, condition2, condition3 };

                BusinessEntityCollection entities = CRMHelper.CRMService.RetrieveMultiple(query);
                if (entities.BusinessEntities.Length > 1)
                    Logger.Write(functionName + "Duplicates detected. More than one contact was found with the e-mail address: " + EmailAddress, "Trace", 3);

                if (entities.BusinessEntities.Length > 0)
                    return (contact)entities.BusinessEntities[0];
                else
                    return null;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        #endregion

        #region "Updating/Creating of CRM entities"

        private void AddAttachmentsToEmail(List<FileAttachmentType> fileAttachments, List<ItemAttachmentType> itemAttachments, Guid emailGuid)
        {
            string functionName = GetType().ToString() + " AddAttachmentsToEmail: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {

                ZipFile zipFile = new ZipFile();
                int fileAttachmentsCount = 0;
                int totalAttachmentsCount = 0;
                Dictionary<Guid, string> inlineAttachments = new Dictionary<Guid, string>();

                if (fileAttachments != null)
                {
                    fileAttachmentsCount = fileAttachments.Count;
                    totalAttachmentsCount = fileAttachments.Count;
                    for (int i = 0; i < fileAttachments.Count; i++)
                    {
                        try
                        {
                            Guid attachmentId = CRMHelper.CreateAttachment(emailGuid.ToString(), fileAttachments[i].Content, GetAttachmentFileName(fileAttachments[i].Name, false), fileAttachments[i].ContentType, i + 1);
                            if (fileAttachments[i].ContentId != null)
                                inlineAttachments.Add(attachmentId, fileAttachments[i].ContentId);
                            zipFile.AddEntry(fileAttachments[i].Name, fileAttachments[i].Content);
                        }
                        catch (SoapException fileSEx)
                        {
                            HandleAttachmentUploadError(fileSEx);
                        }
                    }
                }
                if (itemAttachments != null)
                {
                    totalAttachmentsCount += itemAttachments.Count;
                    for (int i = 0; i < itemAttachments.Count; i++)
                    {
                        try
                        {
                            byte[] byteData = GetItemAttachmentByteData(itemAttachments[i]);
                            if (byteData != null)
                            {
                                string attachmentName = GetAttachmentFileName(itemAttachments[i].Name, true);
                                Guid attachmentId = CRMHelper.CreateAttachment(emailGuid.ToString(), byteData, attachmentName, itemAttachments[i].ContentType, i + 1 + fileAttachmentsCount);
                                if (itemAttachments[i].ContentId != null)
                                    inlineAttachments.Add(attachmentId, itemAttachments[i].ContentId);

                                if (!zipFile.ContainsEntry(attachmentName))
                                    zipFile.AddEntry(attachmentName, byteData);
                                else
                                {
                                    for (int j = 0; j < 20; j++) //surely someone wouldn't attach more than 20 files with the same name?
                                    {
                                        string filenameWithIndex = attachmentName.Substring(0, attachmentName.IndexOf(".eml")) + "(" + (j + 1).ToString() + ")" + ".eml";
                                        if (!zipFile.ContainsEntry(filenameWithIndex))
                                        {
                                            zipFile.AddEntry(filenameWithIndex, byteData);
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                        catch (SoapException itemSEx)
                        {
                            HandleAttachmentUploadError(itemSEx);
                        }
                    }
                }

                CreateZipAttachment(emailGuid, zipFile, totalAttachmentsCount);

                if (inlineAttachments.Count > 0)
                {
                    //Change email description to allow for embedding of images
                    email email = (email)CRMHelper.GetSingleData(EntityName.email.ToString(), emailGuid.ToString());
                    if (!string.IsNullOrEmpty(email.description))
                    {
                        foreach (Guid key in inlineAttachments.Keys)
                        {
                            if (email.description.Contains("cid:" + inlineAttachments[key].ToString()))
                                email.description = email.description.Replace("cid:" + inlineAttachments[key].ToString(),
                                        "/MicrosoftCRM/Activities/Attachment/download.aspx?AttachmentType=1001&AttachmentId=" + key.ToString());
                        }
                        CRMHelper.CallCRMMethod(email, TypeOfCRMRequest.Update);
                    }
                }
            }
            catch (SoapException sEx)
            {
                HandleAttachmentUploadError(sEx);
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void HandleAttachmentUploadError(SoapException sEx)
        {
            string functionName = GetType().ToString() + " HandleAttachmentUploadError: ";
            Logger.Write(functionName + "Enter", "Trace", 3);

            string errorValue = string.Empty;
            if (sEx.Detail != null)
                errorValue = sEx.Detail.InnerText.ToString().ToLower();
            else
                errorValue = sEx.ToString().ToLower();
            
            if (errorValue.Contains("file name") && errorValue.Contains("is invalid"))
                Logger.Write(functionName + "Warning: Invalid attachment file name: " + errorValue, "Warning", 4, 999, TraceEventType.Warning);
            else if (errorValue.Contains("an unexpected error occurred"))
                Logger.Write(functionName + "Warning: Unexpected error from CRM while trying to attach file: " + errorValue, "Warning", 4, 999, TraceEventType.Warning);
            else if (errorValue.Contains("maximum request length exceeded"))
                Logger.Write(functionName + "Warning: File is too big to attach: " + errorValue, "Warning", 4, 999, TraceEventType.Warning);
            else
            {
                Logger.Write(functionName + "Soap Error: " + errorValue, "Error", 5, 999, TraceEventType.Error);
                throw sEx;
            }

            Logger.Write(functionName + "Exit", "Trace", 3);
        }

        private void CreateZipAttachment(Guid emailGuid, ZipFile zipFile, int totalAttachmentsCount)
        {
            string functionName = GetType().ToString() + " CreateZipAttachment: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                MemoryStream outputStream = new MemoryStream();
                zipFile.Save(outputStream);
                outputStream.Seek(0, SeekOrigin.Begin);
                byte[] outputBytes = new byte[outputStream.Length];
                outputStream.Read(outputBytes, 0, int.Parse(outputStream.Length.ToString()));
                CRMHelper.CreateAttachment(emailGuid.ToString(), outputBytes, "attachments.zip", "application/zip", totalAttachmentsCount + 1);
            }
            catch (SoapException sEx)
            {
                HandleAttachmentUploadError(sEx);
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Warning: Could not create Zip attachment: " + ex.ToString(), "Warning", 4, 999, TraceEventType.Warning);
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private string GetAttachmentFileName(string attachmentName, bool IsItemAttachment)
        {
            string functionName = GetType().ToString() + " GetAttachmentFileName: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                Logger.Write(functionName + "Attachment name: " + attachmentName, "Trace", 3);
                string emailFileName = string.Empty;
                if (IsItemAttachment)
                {
                    if (attachmentName.EndsWith(".eml") || attachmentName.EndsWith(".msg"))
                        emailFileName = UtilityHelper.StripPunctuation(attachmentName.Substring(0, attachmentName.Length - 4));
                    else
                        emailFileName = UtilityHelper.StripPunctuation(attachmentName);
                    emailFileName = emailFileName + ".eml";
                }
                else
                {
                    if (attachmentName.Contains("."))
                    {
                        string fileExtension = attachmentName.Substring(attachmentName.LastIndexOf("."));
                        emailFileName = UtilityHelper.StripPunctuation(attachmentName.Substring(0, attachmentName.Length - fileExtension.Length)) + fileExtension;
                    }
                    else
                        emailFileName = UtilityHelper.StripPunctuation(attachmentName);
                }
                Logger.Write(functionName + "Returning name: " + emailFileName, "Trace", 3);
                return emailFileName;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private byte[] GetItemAttachmentByteData(ItemAttachmentType itemAttachment)
        {
            string functionName = GetType().ToString() + " GetItemAttachmentByteData: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {

                string chilkatLicense = ConfigurationManager.AppSettings.Get("chilkatlicense");
                if (string.IsNullOrEmpty(chilkatLicense))
                {
                    Logger.Write(functionName + "Warning: Attached item will not be included in CRM mail: No ChilKat license available", "Warning", 4, 999, TraceEventType.Warning);
                    return null;
                }

                MessageType recEmail = (MessageType)itemAttachment.Item;
                Chilkat.Email email = new Chilkat.Email();
                email.UnlockComponent(chilkatLicense);
                email.Body = (itemAttachment.Item.Body.Value);
                email.Subject = itemAttachment.Item.Subject;

                if (recEmail.From != null)
                    email.From = recEmail.From.Item.Name + "<" + recEmail.From.Item.EmailAddress + ">";
                if (recEmail.ToRecipients != null)
                    email.AddTo(recEmail.ToRecipients[0].Name, recEmail.ToRecipients[0].EmailAddress);

                string fileName = ConfigurationManager.AppSettings.Get("tempsavefiledirectory") + Guid.NewGuid().ToString() + ".eml";
                email.SaveEml(fileName);

                FileInfo pointer = new FileInfo(fileName);
                FileStream fileStream = pointer.OpenRead();
                byte[] byteData = new byte[(int)fileStream.Length];
                fileStream.Read(byteData, 0, (int)fileStream.Length);
                fileStream.Flush();
                fileStream.Close();
                File.Delete(fileName);
                return byteData;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Warning: Conversion of Item to ByteArray failed: " + ex.ToString(), "Warning", 4, 999, TraceEventType.Warning);
                return null;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void AddEmailToQueue(MessageType ReceivedEmail, List<FileAttachmentType> FileAttachments, List<ItemAttachmentType> ItemAttachments, queue Queue)
        {
            string functionName = GetType().ToString() + " AddEmailToQueue: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                Logger.Write(functionName + "Setting email properties", "Trace", 1);
                email Email = SetEmailProperties(ReceivedEmail, Queue);
                Logger.Write(functionName + "Attempting create", "Trace", 1);
                Guid emailGuid = new Guid();
                try
                {
                    emailGuid = CRMHelper.CallCRMMethod(Email, TypeOfCRMRequest.Create);
                }
                catch (SoapException mailEx)
                {
                    string errorValue = string.Empty;
                    if (mailEx.Detail != null)
                        errorValue = mailEx.Detail.InnerText.ToString();
                    else
                        errorValue = mailEx.ToString();
                    errorValue = errorValue.ToLower();
                    if (errorValue.Contains("invalid e-mail address."))
                        Logger.Write(functionName + "Warning: Invalid e-mail address error: " + errorValue, "Warning", 4, 999, TraceEventType.Warning);
                    else
                        throw;
                }
                if (emailGuid != new Guid())
                {
                    if (FileAttachments.Count > 0 || ItemAttachments.Count > 0)
                        AddAttachmentsToEmail(FileAttachments, ItemAttachments, emailGuid);
                    RouteEmailToQueue(emailGuid, new Guid(_queueSettings.QueueID));
                }
            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private email SetEmailProperties(MessageType ReceivedEmail, queue Queue)
        {
            string functionName = GetType().ToString() + " SetEmailProperties: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                Logger.Write(functionName + "Creating 'from' entity", "Trace", 1);
                activityparty from = new activityparty();
                //Remove any punctuation from address name email name
                from.addressused = UtilityHelper.StripPunctuation(ReceivedEmail.From.Item.Name) + "<" + ReceivedEmail.From.Item.EmailAddress + ">";

                Logger.Write(functionName + "Creating email object", "Trace", 1);
                email email = new email();

                Logger.Write(functionName + "Setting from", "Trace", 1);
                email.from = new activityparty[] { from };

                Logger.Write(functionName + "Setting actualend value", "Trace", 1);
                email.actualend = new CrmDateTime();
                email.actualend.Value = DateTime.Now.ToString("s");

                Logger.Write(functionName + "Setting to", "Trace", 1);
                email.to = GenerateEmailToAddresses(ReceivedEmail, Queue).ToArray();

                Logger.Write(functionName + "Setting cc", "Trace", 1);
                List<activityparty> ccRecipients = new List<activityparty>();
                if (ReceivedEmail.CcRecipients != null && ReceivedEmail.CcRecipients.GetLength(0) > 0)
                {
                    foreach (EmailAddressType ccRecipient in ReceivedEmail.CcRecipients)
                    {
                        activityparty recipient = new activityparty();
                        recipient.addressused = UtilityHelper.StripPunctuation(ccRecipient.Name) + "<" + ccRecipient.EmailAddress + ">";
                        ccRecipients.Add(recipient);
                    }
                }
                if (ccRecipients.Count > 0)
                    email.cc = ccRecipients.ToArray();

                Logger.Write(functionName + "Setting subject", "Trace", 1);
                if (string.IsNullOrEmpty(ReceivedEmail.Subject))
                    email.subject = "No subject";
                else
                    email.subject = CRMHelper.FormatEntityAttributeValue(EntityName.email.ToString(), "subject", ReceivedEmail.Subject);

                Logger.Write(functionName + "Setting headers", "Trace", 1);
                string headers = string.Empty;
                if (ReceivedEmail.InternetMessageHeaders != null)
                    foreach (InternetHeaderType header in ReceivedEmail.InternetMessageHeaders)
                        headers += header.HeaderName + ": " + header.Value + Environment.NewLine;
                if (headers.Length > 4000)
                    headers = headers.Substring(0, 3999);
                //email.new_headers = headers;

                Logger.Write(functionName + "Setting description", "Trace", 1);
                if (ReceivedEmail.Body != null && ReceivedEmail.Body.Value != null)
                {
                    email.description = ReceivedEmail.Body.Value;
                    if (ReceivedEmail.Body.BodyType1 == BodyTypeType.Text)
                    {
                        Logger.Write(functionName + "Formatting text description", "Trace", 1);
                        email.description = email.description.Replace("<", "&lt;");
                        email.description = email.description.Replace(">", "&gt;");
                        email.description = email.description.Replace("\"", "&quot;");
                        email.description = email.description.Replace(Environment.NewLine, "<br />");
                    }
                }
                else
                    email.description = string.Empty;

                Logger.Write(functionName + "Removing troublesome characters", "Trace", 1);
                email.description = RemoveTroublesomeCharacters(email.description);
                Logger.Write(functionName + "Troublesome characters removed", "Trace", 1);

                Logger.Write(functionName + "Setting directioncode", "Trace", 1);
                email.directioncode = new CrmBoolean();
                email.directioncode.Value = false; //incoming

                Logger.Write(functionName + "Setting owner", "Trace", 1);
                email.ownerid = new Owner();
                email.ownerid.type = EntityName.systemuser.ToString();
                email.ownerid.Value = Queue.primaryuserid.Value;

                return email;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }


        public static string RemoveTroublesomeCharacters(string inString)
        {
            if (inString == null) return null;
            StringBuilder newString = new StringBuilder();
            char ch;
            for (int i = 0; i < inString.Length; i++)
            {
                ch = inString[i];
                // remove any characters outside the valid UTF-8 range as well as all control characters
                // except tabs and new lines
                if ((ch < 0x00FD && ch > 0x001F) || ch == '\t' || ch == '\n' || ch == '\r')
                    newString.Append(ch);
            }
            return newString.ToString();
        }

        private List<activityparty> GenerateEmailToAddresses(MessageType ReceivedEmail, queue Queue)
        {
            string functionName = GetType().ToString() + " GenerateEmailToAddresses: ";
            Logger.Write(functionName + "Enter", "Trace", 3);

            try
            {
                activityparty to = new activityparty();
                to.partyid = new Lookup();
                to.partyid.Value = new Guid(_queueSettings.QueueID);
                to.partyid.type = EntityName.queue.ToString();
                to.participationtypemask = new Picklist();
                to.participationtypemask.Value = 10; // resource

                List<activityparty> toAddresses = new List<activityparty>();
                toAddresses.Add(to);
                try
                {
                    if (ReceivedEmail.ToRecipients != null && ReceivedEmail.ToRecipients.GetLength(0) > 0)
                        for (int i = 0; i < ReceivedEmail.ToRecipients.GetLength(0); i++)
                            if (ReceivedEmail.ToRecipients[i] != null &&
                                ReceivedEmail.ToRecipients[i].EmailAddress != null &&
                                ReceivedEmail.ToRecipients[i].EmailAddress != Queue.emailaddress)
                            {
                                QueryExpression query = new QueryExpression();
                                query.EntityName = "queue";
                                query.ColumnSet = new AllColumns();
                                query.Criteria = new FilterExpression();
                                query.Criteria.FilterOperator = LogicalOperator.And;
                                ConditionExpression condition1 = new ConditionExpression();
                                condition1.AttributeName = "emailaddress";
                                condition1.Operator = ConditionOperator.Equal;
                                condition1.Values = new object[] { ReceivedEmail.ToRecipients[i].EmailAddress };
                                query.Criteria.Conditions = new ConditionExpression[] { condition1 };

                                BusinessEntityCollection entities = CRMHelper.CRMService.RetrieveMultiple(query);
                                if (entities.BusinessEntities != null && entities.BusinessEntities.GetLength(0) > 0)
                                {
                                    queue alsoSentToQueue = (queue)entities.BusinessEntities[0];
                                    activityparty alsoTo = new activityparty();
                                    alsoTo.partyid = new Lookup();
                                    alsoTo.partyid.Value = alsoSentToQueue.queueid.Value;
                                    alsoTo.partyid.type = EntityName.queue.ToString();
                                    alsoTo.participationtypemask = new Picklist();
                                    alsoTo.participationtypemask.Value = 10; // resource
                                    if (toAddresses.Contains(alsoTo))
                                        toAddresses.Add(alsoTo);
                                }
                            }
                }
                catch (Exception tEx)
                {
                    Logger.Write(functionName + "Warning: Could not add multiple recipients to email: " + tEx.ToString(), "Warning", 4, 999, TraceEventType.Warning);
                }
                return toAddresses;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void SetEmailStatusToCompleted(Guid emailGuid)
        {
            string functionName = GetType().ToString() + " SetEmailStatusToCompleted: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Set state to completed
                SetStateEmailRequest setState = new SetStateEmailRequest();
                setState.EmailState = EmailState.Completed;
                setState.EmailStatus = 4;
                setState.EntityId = emailGuid;
                CRMHelper.CallCRMMethod(setState, TypeOfCRMRequest.Execute);
            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void DetachEmailFromQueue(email Email, string QueueId)
        {
            string functionName = GetType().ToString() + " DetachEmailFromQueue: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                BusinessEntity[] queryResult;
                if (!string.IsNullOrEmpty(QueueId))
                {
                    queryResult = CRMHelper.GetEntitiesByAttributes(
                                        EntityName.queueitem.ToString(), 
                                        new string[] { "objectid", "queueid" }, 
                                        new string[] { Email.activityid.Value.ToString(), QueueId });

                }
                else
                {
                    queryResult = CRMHelper.GetEntitiesByAttributes(
                                        EntityName.queueitem.ToString(),
                                        new string[] { "objectid"},
                                        new string[] { Email.activityid.Value.ToString()});

                }
                foreach (BusinessEntity businessEntity in queryResult)
                {
                    queueitem queueItem = (queueitem)businessEntity;
                    DetachFromQueueEmailRequest detachRequest = new DetachFromQueueEmailRequest();
                    detachRequest.EmailId = Email.activityid.Value;
                    detachRequest.QueueId = queueItem.queueid.Value;
                    CRMHelper.CallCRMMethod(detachRequest, TypeOfCRMRequest.Execute);
                }
            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void GetQueueOwnerWIPQueue(queue Queue)
        {
            string functionName = GetType().ToString() + " GetQueueOwnerWIPQueue: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                QueryExpression query = new QueryExpression();
                query.EntityName = "queue";
                query.ColumnSet = new AllColumns();
                query.Criteria = new FilterExpression();
                query.Criteria.FilterOperator = LogicalOperator.And;
                ConditionExpression condition1 = new ConditionExpression();
                condition1.AttributeName = "primaryuserid";
                condition1.Operator = ConditionOperator.Equal;
                condition1.Values = new object[] { Queue.primaryuserid.Value.ToString() };
                ConditionExpression condition2 = new ConditionExpression();
                condition2.AttributeName = "queuetypecode";
                condition2.Operator = ConditionOperator.Equal;
                condition2.Values = new object[] { "3" }; //private queue
                query.Criteria.Conditions = new ConditionExpression[] { condition1, condition2 };
                BusinessEntityCollection entities = CRMHelper.CRMService.RetrieveMultiple(query);
                _queueOwnerWipQueue = (queue)entities.BusinessEntities[0];
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void RouteCase(Guid CaseId)
        {
            string functionName = GetType().ToString() + " RouteCase: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Route the case to the correct queue
                CRMHelper.RouteCase(CaseId, _queueOwnerWipQueue.queueid.Value.ToString(), _queueSettings.QueueID);
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void RouteEmailToQueue(Guid EmailId, Guid TargetQueueId)
        {
            string functionName = GetType().ToString() + " RouteEmailToQueue: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {

                BusinessEntity[] queryResult = CRMHelper.GetEntitiesByAttributes(
                                        EntityName.queueitem.ToString(),
                                        new string[] { "objectid" },
                                        new string[] { EmailId.ToString() });

                // Execute the retrieval.
                if (queryResult == null)
                    throw new Exception("Could not retrieve current queue for email being routed from Exchange: " + EmailId);

                queueitem qitem = (queueitem)queryResult[0];
                TargetQueuedEmail tqt = new TargetQueuedEmail();
                tqt.EntityId = EmailId;
                RouteRequest route = new RouteRequest();
                route.Target = tqt;
                route.RouteType = RouteType.Queue;
                route.SourceQueueId = qitem.queueid.Value;
                route.EndpointId = TargetQueueId;
                CRMHelper.CallCRMMethod(route, TypeOfCRMRequest.Execute);
            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void SetCaseProperties(IncomingEntity IncomingEntity, email Email, queue Queue, DynamicEntity CaseEntity)
        {
            string functionName = GetType().ToString() + " SetCaseProperties: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                ArrayList caseProperties = new ArrayList();
                //Type is incident
                Logger.Write(functionName + "Setting title to: " + Email.subject, "Trace", 1);
                CaseEntity.Name = EntityName.incident.ToString();
                //The title should be the subject of the e-mail
                StringProperty titleProperty = new StringProperty();
                titleProperty.Name = "title";
                titleProperty.Value = Email.subject;
                caseProperties.Add(titleProperty);

                //Set the customer
                Logger.Write(functionName + "Setting customerid to: " + IncomingEntity.CaseEntityID.ToString(), "Trace", 1);
                CustomerProperty customer = new CustomerProperty();
                customer.Name = "customerid";
                customer.Value = new Customer();
                customer.Value.type = IncomingEntity.CaseEntityType;
                customer.Value.Value = IncomingEntity.CaseEntityID;
                caseProperties.Add(customer);

                //Set the owner
                Logger.Write(functionName + "Setting ownerid to: " + Queue.primaryuserid.Value.ToString(), "Trace", 1);
                OwnerProperty owner = new OwnerProperty();
                owner.Name = "ownerid";
                owner.Value = new Owner();
                owner.Value.type = EntityName.systemuser.ToString();
                owner.Value.Value = Queue.primaryuserid.Value;
                caseProperties.Add(owner);

                //Set the customer's email address - non-standard customization - Add field to CRM & uncomment to use
                //Logger.Write(functionName + "Setting new_customeremailaddress to: " + Email.from[0].addressused, "Trace", 1);
                //StringProperty customerEmailAddress = new StringProperty();
                //customerEmailAddress.Name = "new_customeremailaddress";
                //customerEmailAddress.Value = Email.from[0].addressused;
                //caseProperties.Add(customerEmailAddress);

                //Should we update the responsible contact field?
                if (_queueSettings.SetResponsibleContact)
                {
                    Logger.Write(functionName + "Setting responsiblecontactid to: " + IncomingEntity.ID.ToString(), "Trace", 1);
                    LookupProperty responsibleContact = new LookupProperty();
                    responsibleContact.Name = "responsiblecontactid";
                    responsibleContact.Value = new Lookup();
                    responsibleContact.Value.type = IncomingEntity.Type;
                    responsibleContact.Value.Value = IncomingEntity.ID;
                    caseProperties.Add(responsibleContact);
                }
                //Should the description be updated?
                if (_queueSettings.UpdateCaseDescription)
                {
                    StringProperty descriptionProperty = new StringProperty();
                    descriptionProperty.Name = "description";
                    //Limit length
                    descriptionProperty.Value = CRMHelper.FormatEntityAttributeValue(EntityName.incident.ToString(), "description", _cleanEmailDescription);
                    caseProperties.Add(descriptionProperty);
                }
                //Set properties
                CaseEntity.Properties = (Property[])caseProperties.ToArray(typeof(Property));
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void SetCaseDynamicProperties(DynamicEntity CaseEntity)
        {
            string functionName = GetType().ToString() + " SetDynamicProperties: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                ArrayList properties = new ArrayList(CaseEntity.Properties);
                foreach (string propertyValues in _queueSettings.CaseAttributes.Split('|'))
                {
                    if (propertyValues.Trim().Length == 0)
                        return;
                    string[] propertyValue = propertyValues.Split('#');
                    Property property = null;
                    if (propertyValue.Length != 2)
                        Logger.Write(functionName + "Dynamic property definition incorrect: " + propertyValues, "Error", 5, 999, TraceEventType.Error);
                    else
                    {
                        //Get the attribute metadata
                        Crm.MetaDataService.AttributeMetadata attibute = CRMHelper.GetAttribute(EntityName.incident.ToString(), propertyValue[0]);
                        //Create the property
                        switch (attibute.AttributeType.Value)
                        {
                            case Crm.MetaDataService.AttributeType.String:
                            case Crm.MetaDataService.AttributeType.Memo:
                                property = new StringProperty();
                                ((StringProperty)property).Name = propertyValue[0];
                                ((StringProperty)property).Value = propertyValue[1];
                                break;
                            case Crm.MetaDataService.AttributeType.Integer:
                                property = new CrmNumberProperty();
                                ((CrmNumberProperty)property).Name = propertyValue[0];
                                ((CrmNumberProperty)property).Value = new CrmNumber();
                                ((CrmNumberProperty)property).Value.Value = int.Parse(propertyValue[1]);
                                break;
                            case Crm.MetaDataService.AttributeType.Float:
                                property = new CrmFloatProperty();
                                ((CrmFloatProperty)property).Name = propertyValue[0];
                                ((CrmFloatProperty)property).Value = new CrmFloat();
                                ((CrmFloatProperty)property).Value.Value = double.Parse(propertyValue[1]);
                                break;
                            case Crm.MetaDataService.AttributeType.Decimal:
                                property = new CrmDecimalProperty();
                                ((CrmDecimalProperty)property).Name = propertyValue[0];
                                ((CrmDecimalProperty)property).Value = new CrmDecimal();
                                ((CrmDecimalProperty)property).Value.Value = decimal.Parse(propertyValue[1]);
                                break;
                            case Crm.MetaDataService.AttributeType.DateTime:
                                property = new CrmDateTimeProperty();
                                ((CrmDateTimeProperty)property).Name = propertyValue[0];
                                ((CrmDateTimeProperty)property).Value = new CrmDateTime();
                                ((CrmDateTimeProperty)property).Value.Value = propertyValue[1];
                                break;
                            case Crm.MetaDataService.AttributeType.Customer:
                                property = new CustomerProperty();
                                ((CustomerProperty)property).Name = propertyValue[0];
                                ((CustomerProperty)property).Value = new Customer();
                                ((CustomerProperty)property).Value.Value = new Guid(propertyValue[1]);
                                break;
                            case Crm.MetaDataService.AttributeType.Boolean:
                                property = new CrmBooleanProperty();
                                ((CrmBooleanProperty)property).Name = propertyValue[0];
                                ((CrmBooleanProperty)property).Value = new CrmBoolean();
                                ((CrmBooleanProperty)property).Value.Value = bool.Parse(propertyValue[1]);
                                break;
                            case Crm.MetaDataService.AttributeType.Status:
                                property = new StatusProperty();
                                ((StatusProperty)property).Name = propertyValue[0];
                                ((StatusProperty)property).Value = new Status();
                                ((StatusProperty)property).Value.Value = int.Parse(propertyValue[1]);
                                break;
                            case Crm.MetaDataService.AttributeType.State:
                                property = new StateProperty();
                                ((StateProperty)property).Name = propertyValue[0];
                                ((StateProperty)property).Value = propertyValue[1];
                                break;
                            case Crm.MetaDataService.AttributeType.Picklist:
                                property = new PicklistProperty();
                                ((PicklistProperty)property).Name = propertyValue[0];
                                ((PicklistProperty)property).Value = new Picklist();
                                ((PicklistProperty)property).Value.Value = int.Parse(propertyValue[1]);
                                break;
                            case Crm.MetaDataService.AttributeType.Lookup:
                                property = new LookupProperty();
                                ((LookupProperty)property).Name = propertyValue[0];
                                ((LookupProperty)property).Value = new Lookup();
                                ((LookupProperty)property).Value.Value = new Guid(propertyValue[1]);
                                break;
                            default:
                                Logger.Write(functionName + "Dynamic property definition incorrect: " + propertyValues, "Error", 5, 999, TraceEventType.Error);
                                break;
                        }
                        if (property != null)
                            properties.Add(property);
                    }

                }
                CaseEntity.Properties = (Property[])properties.ToArray(typeof(Property));
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private bool SetCaseCustomer(contact Contact, IncomingEntity IncomingEntity)
        {
            string functionName = GetType().ToString() + " SetCaseCustomer: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Check if the option is set
                if (_queueSettings.MapCaseToAccount)
                {
                    //Check to see if the contact has a parent account
                    if (Contact.parentcustomerid != null)
                    {
                        //Then use the parent account
                        IncomingEntity.CaseEntityID = Contact.parentcustomerid.Value;
                        IncomingEntity.CaseEntityType = Contact.parentcustomerid.type;
                    }
                    else
                    {
                        IncomingEntity.CaseEntityID = IncomingEntity.ID;
                        IncomingEntity.CaseEntityType = IncomingEntity.Type;
                    }
                }
                else
                {
                    IncomingEntity.CaseEntityID = IncomingEntity.ID;
                    IncomingEntity.CaseEntityType = IncomingEntity.Type;
                }
                return true;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void UpdateCaseTitle(Guid caseId)
        {
            string functionName = GetType().ToString() + " UpdateCaseTitle: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                incident caseIncident = (incident)CRMHelper.GetSingleData(EntityName.incident.ToString(), caseId.ToString());
                string newTitle = caseIncident.ticketnumber + " - " + caseIncident.title;
                Logger.Write(functionName + "Setting title to: " + newTitle, "Trace", 1);
                caseIncident.title = CRMHelper.FormatEntityAttributeValue(EntityName.incident.ToString(), "title", newTitle);
                CRMHelper.CallCRMMethod(caseIncident, TypeOfCRMRequest.Update);
            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void UpdateEmailWithCaseDetails(email Email, Guid CaseId)
        {
            string functionName = GetType().ToString() + " UpdateEmailWithCaseDetails: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Update the e-mail and set "regarding" to the case
                Email.regardingobjectid = new Lookup();
                Email.regardingobjectid.type = EntityName.incident.ToString();
                Email.regardingobjectid.Value = CaseId;
                //Update e-mail
                CRMHelper.CallCRMMethod(Email, TypeOfCRMRequest.Update);

            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void UpdateEmailSenderWithContact(email Email, Guid contactId)
        {
            string functionName = GetType().ToString() + " UpdateEmailSenderWithContact: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Update the from entity on the Email to the created contact
                Email.from[0].partyid = new Lookup();
                Email.from[0].partyid.type = EntityName.contact.ToString();
                Email.from[0].partyid.Value = contactId;
                CRMHelper.CallCRMMethod(Email, TypeOfCRMRequest.Update);
            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void UpdateEmailHeaders(email Email)
        {
            string functionName = GetType().ToString() + " UpdateEmailHeaders: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                if (_exchangeHelper == null)
                    _exchangeHelper = new Exchange2007Helper(ConfigurationManager.AppSettings.Get("exchangeserviceurl"),
                                                            _queueSettings.ExchangeDomain,
                                                            _queueSettings.ExchangeUsername,
                                                            _queueSettings.ExchangePassword);
                //Email.new_headers = _exchangeHelper.GetEmailHeaders(Email.from[0].addressused, Email.subject);
                CRMHelper.CallCRMMethod(Email, TypeOfCRMRequest.Update);
            }
            catch (SoapException sEx)
            {
                if (sEx.Detail != null)
                    Logger.Write(functionName + "Error: " + sEx.Detail.InnerText.ToString(), "Error", 5, 999, TraceEventType.Error);
                else
                    Logger.Write(functionName + "Error: " + sEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        #endregion

        #region "Sending of emails"

        private void SendUpdateEmail(email Email, queue Queue, incident CaseEntity)
        {
            string functionName = GetType().ToString() + " SendUpdateEmail: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Check whether or not the incident is owned by a systemuser and not by a queue or something else
                if (CaseEntity.ownerid.type != EntityName.systemuser.ToString())
                {
                    Logger.Write(functionName + "Incident is not owner by a system user: not sending update email", "Trace", 3);
                    return;
                }
                //if the queue's owner and the incident's owner is the same it means that the incident hasn't yet been assigned to someone
                if (Queue.primaryuserid.Value == CaseEntity.ownerid.Value)
                {
                    Logger.Write(functionName + "Incident is owned by primary user of queue: not sending update email", "Trace", 3);
                    return;
                }

                systemuser systemUser = (systemuser)CRMHelper.GetSingleData(EntityName.systemuser.ToString(), CaseEntity.ownerid.Value.ToString());
                incident incident = (incident)CaseEntity;
                Logger.Write(functionName + "Sending Case Updated Mail to " + systemUser.firstname + " " + systemUser.lastname, "Trace", 1);
                IncomingEntity incomingEntity = new IncomingEntity();
                incomingEntity.CaseEntityID = systemUser.systemuserid.Value;
                incomingEntity.CaseEntityType = EntityName.systemuser.ToString();
                incomingEntity.FirstName = systemUser.firstname;
                incomingEntity.LastName = systemUser.lastname;
                incomingEntity.Contact = null;
                incomingEntity.ID = systemUser.systemuserid.Value;
                incomingEntity.Type = EntityName.systemuser.ToString();

                //Parse the text
                string emailSubject = ParseCaseMail(_queueSettings.UpdateMailSubject, CaseEntity, incomingEntity, Email, Queue);
                string emailBody = ParseCaseMail(File.ReadAllText(_queueSettings.UpdateMailTemplate), CaseEntity, incomingEntity, Email, Queue);
                //If the setting casemail-from is a mail address then throw an exception
                if (_queueSettings.CaseMailFrom.Contains("@"))
                    throw new Exception("UpdateMails can only be sent from queues");
                else
                    //...otherwise, send using CRM.
                    CRMHelper.SendCRMMail(emailSubject, emailBody, incomingEntity.Type, incomingEntity.ID.ToString(), _queueSettings.UpdateMailFromType, _queueSettings.UpdateMailFrom, EntityName.incident.ToString(), CaseEntity.incidentid.Value.ToString(), true);
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private void SendCaseEmails(IncomingEntity IncomingEntity, email Email, queue Queue, incident CaseEntity)
        {
            string functionName = GetType().ToString() + " SendCaseEmails: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Send an e-mail
                if (_queueSettings.SendCaseMail)
                {
                    Logger.Write(functionName + "Sending Case Mail", "Trace", 1);
                    //Parse the text
                    string emailSubject = ParseCaseMail(_queueSettings.CaseMailSubject, CaseEntity, IncomingEntity, Email, Queue);
                    string emailBody = ParseCaseMail(File.ReadAllText(_queueSettings.CaseMailTemplate), CaseEntity, IncomingEntity, Email, Queue);
                    //If the setting casemail-from is a mail addrees then send using Net mail...
                    if (_queueSettings.CaseMailFrom.Contains("@"))
                        UtilityHelper.SendNetMail(emailSubject, emailBody, Email.from[0].addressused, _queueSettings.CaseMailNotify, _queueSettings.CaseMailFrom, _queueSettings.CaseMailUseHtml);
                    else
                    {
                        //...otherwise, send using CRM.
                        CRMHelper.SendCRMMail(emailSubject, emailBody, IncomingEntity.Type, IncomingEntity.ID.ToString(), _queueSettings.CaseMailFromType, _queueSettings.CaseMailFrom, EntityName.incident.ToString(), CaseEntity.incidentid.Value.ToString().ToLower(), true);
                        //Send a notification that a case has been created?
                        if (!string.IsNullOrEmpty(_queueSettings.CaseMailNotify))
                            UtilityHelper.SendNetMail(emailSubject, emailBody, _queueSettings.CaseMailNotify, null, _queueSettings.CaseMailFrom, _queueSettings.CaseMailUseHtml);
                    }
                }
                //Send a separate notification?
                if (_queueSettings.SendCaseNotification)
                {
                    Logger.Write(functionName + "Sending Case Notification", "Trace", 1);
                    //Parse the text
                    string emailSubject = ParseCaseMail(_queueSettings.CaseNotificationSubject, CaseEntity, IncomingEntity, Email, Queue);
                    string emailBody = ParseCaseMail(File.ReadAllText(_queueSettings.CaseNotificationTemplate), CaseEntity, IncomingEntity, Email, Queue);
                    //Send the notification mail
                    UtilityHelper.SendNetMail(emailSubject, emailBody, _queueSettings.CaseNotificationTo, "", _queueSettings.CaseNotificationFrom, _queueSettings.CaseNotificationUseHtml);
                }
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }
       
        #endregion

        #region "Setting up of IncomingEntity"

        private IncomingEntity SetupIncomingEntity(email Email)
        {
            string functionName = GetType().ToString() + " SetupIncomingEntity: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                IncomingEntity incomingEntity = new IncomingEntity();
                //If partyid is nothing then the e-mail isn't related to a contact
                contact contact = null;
                if (Email.from[0].partyid == null)
                    contact = GetContactFromEmailAddress(Email.from[0].addressused);
                else
                    if (Email.from[0].partyid.type == EntityName.systemuser.ToString())
                    {
                        _sendEmails = false;
                        contact = (contact)CRMHelper.GetSingleData(EntityName.contact.ToString(), ConfigurationManager.AppSettings.Get("defaultAccountId"));
                    }
                    else if (Email.from[0].partyid.type == EntityName.lead.ToString())
                        contact = GetContactFromEmailAddress(Email.from[0].addressused);
                    else if (Email.from[0].partyid.type == EntityName.contact.ToString())
                        contact = (contact)CRMHelper.GetSingleData(EntityName.contact.ToString(), Email.from[0].partyid.Value.ToString());
                    else if (Email.from[0].partyid.type == EntityName.account.ToString())
                        return SetupIncomingEntityFromSubmittedBy(Email);
                    else
                        throw new Exception("Could not derive contact from 'from' address for email: " + Email.subject);

                if (contact == null && _queueSettings.CreateContact)
                    contact = CreateContact(Email);
                else if (contact == null)
                    throw new Exception("Could not resolve contact for email and CreateContact is set to false.");

                return SetupIncomingEntityFromContact(contact);
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private IncomingEntity SetupIncomingEntityFromSubmittedBy(email Email)
        {
            string functionName = GetType().ToString() + " SetupEntityFromSubmittedBy: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                NameStruct name = UtilityHelper.ResolveNames(Email.submittedby);
                return new IncomingEntity()
                {
                    FirstName = name.FirstName,
                    LastName = name.LastName,
                    ID = Email.from[0].partyid.Value,
                    Type = EntityName.account.ToString(),
                    CaseEntityID = Email.from[0].partyid.Value,
                    CaseEntityType = EntityName.account.ToString()
                };
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private IncomingEntity SetupIncomingEntityFromContact(contact Contact)
        {
            string functionName = GetType().ToString() + " SetIncomingEntityProperties: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Create a generic representation of the type used for parsing etc
                IncomingEntity incomingEntity = new IncomingEntity()
                {
                    FirstName = Contact.firstname,
                    LastName = Contact.lastname,
                    ID = Contact.contactid.Value,
                    Type = EntityName.contact.ToString(),
                    Contact = Contact
                };
                SetCaseCustomer(Contact, incomingEntity);
                return incomingEntity;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        #endregion

        #region "Determinging whether or not to create a new case"

        private bool CreateNewCaseFromEmail(queue Queue, email Email)
        {
            string functionName = GetType().ToString() + " ApplyConfigurationValidationRules: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //Case number check
                if (_queueSettings.MatchCaseTicketNumber)
                    //Try and get the case if there's a match for the case number in the title.
                    if (EmailSubjectContainsExistingCaseReference(Email, Queue))
                        return false;
                
                return true;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private bool EmailSubjectContainsIgnoredWords(string subject)
        {
            string functionName = GetType().ToString() + " EmailSubjectContainsIgnoredWords: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                if (subject == null)
                    return false;

                foreach (string word in IgnoreWhenSubjectContains)
                    //Check if the subject contains any of the words
                    if (subject.ToLower().Contains(word.ToLower()))
                        return true;
                return false;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        private bool EmailSubjectContainsExistingCaseReference(email Email, queue Queue)
        {
            string functionName = GetType().ToString() + " EmailSubjectContainsExistingCaseReference: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                //See whether regex matches case number in email subject
                if (_caseIdentifierRegEx.IsMatch(Email.subject))
                {
                    string caseNumber = _caseIdentifierRegEx.Match(Email.subject).Value;

                    BusinessEntity[] queryResult =  CRMHelper.GetEntitiesByAttributes(
                                                        EntityName.incident.ToString(), 
                                                        new string[] { "ticketnumber" },
                                                        new string[] { caseNumber });
                    if (queryResult != null)
                    {
                        UpdateEmailWithCaseDetails(Email, ((incident)queryResult[0]).incidentid.Value);
                        UpdateEmailSenderWithContact(Email, ((incident)queryResult[0]).customerid.Value);
                        //Remove the e-mail from the queue if the e-mail already is related to a case
                        if (_queueSettings.RemoveRepliesFromQueue)
                            DetachEmailFromQueue(Email, Queue.queueid.Value.ToString());
                        SetEmailStatusToCompleted(Email.activityid.Value);
                        if (_queueSettings.SendUpdateMail)
                            SendUpdateEmail(Email, Queue, (incident)queryResult[0]);
                        return true;
                    }
                }
                return false;
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        #endregion

        #region "Routing of emails from exchange inbox"

        private void RouteEmailsFromExchange(queue queue)
        {
            string functionName = GetType().ToString() + " RouteEmailsFromExchange: ";
            Logger.Write(functionName + "Enter", "Trace", 3);
            try
            {
                Logger.Write(functionName + "Getting items for Exchange user: " + _queueSettings.ExchangeDomain + "\\" + _queueSettings.ExchangeUsername, "Trace", 1);
                _exchangeHelper = ExchangeHelpers[queue.queueid.Value.ToString().ToLower()];
                //Get all unread emails
                List<MessageType> emails = _exchangeHelper.GetFolderItems("Inbox");
                if (emails == null || emails.Count == 0)
                {
                    Logger.Write(functionName + "No emails found", "Trace", 1);
                    return;
                }
                else
                {
                    Logger.Write(functionName + "Got " + emails.Count.ToString() + " Emails", "Trace", 1);
                    foreach (MessageType email in emails)
                    {
                        try
                        {
                            Logger.Write(functionName + "Processing email from " + email.From.Item.EmailAddress, "Trace", 1);
                            if (string.IsNullOrEmpty(email.From.Item.EmailAddress))
                                if (!string.IsNullOrEmpty(email.Subject))
                                    Logger.Write(functionName + "Warning: Cannot process mail due to EmailAddress being empty. Email subject: " + email.Subject, "Warning", 4, 999, TraceEventType.Warning);
                                else
                                    Logger.Write(functionName + "Warning: Cannot process mail due to EmailAddress being empty.", "Warning", 4, 999, TraceEventType.Warning);
                            else if (IgnoreEmailFrom.Contains(email.From.Item.EmailAddress.Trim().ToLower()))
                                Logger.Write(functionName + "Ignoring email from: " + email.From.Item.EmailAddress + " due to configuration setting ignoreemailsfrom", "Trace", 3);
                            else if (EmailSubjectContainsIgnoredWords(email.Subject))
                                Logger.Write(functionName + "Ignoring email from: " + email.From.Item.EmailAddress + " with subject " + email.Subject + " due to configuration of ignored words in subject", "Trace", 3);
                            else
                            {
                                List<FileAttachmentType> fileAttachments = new List<FileAttachmentType>();
                                List<ItemAttachmentType> itemAttachments = new List<ItemAttachmentType>();
                                _exchangeHelper.GetAttachmentsInMessage(email.ItemId, out fileAttachments, out itemAttachments);
                                AddEmailToQueue(email, fileAttachments, itemAttachments, queue);
                            }
                        }
                        catch (Exception mailEx)
                        {
                            Logger.Write(functionName + "Error: " + mailEx.ToString(), "Error", 5, 999, TraceEventType.Error);
                        }
                        finally
                        {
                            //Set the item to read so we don't try to process it again regardless of whether we got an error
                            try
                            {
                                _exchangeHelper.SetItemReadStatus(email.ItemId, true);
                            }
                            catch (Exception readEx)
                            {
                                Logger.Write(functionName + "Warning: Could not set item read status. Error: " + readEx.ToString(), "Warning", 4, 999, TraceEventType.Warning);
                            }

                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Write(functionName + "Error: " + ex.ToString(), "Error", 5, 999, TraceEventType.Error);
                throw;
            }
            finally
            {
                Logger.Write(functionName + "Exit", "Trace", 3);
            }
        }

        #endregion

    }
}
